from flask import Flask, jsonify, request, render_template, make_response, send_from_directory, abort
from utils.log_output import simple_text_log
from DBReader import DBReader
from StatusManager import StatusManager
from ConnectManager import ConnectManager
from utils.utils import get_current_upload_list
import json
import time
import os
from requests_toolbelt.multipart import decoder


app = Flask(__name__, template_folder='app/templates/', static_folder='app/static')
app.config.update(MAX_CONTENT_LENGTH=160*1024*1024)

cur_db_file = 'DB/DB_20210430_155523.db'

# Initialization
simple_text_log('frontend', 'Initializing server...')
db_reader = DBReader(cur_db_file)
status_manager = StatusManager()
connect_manager = ConnectManager()
simple_text_log('frontend', 'Finished')


@app.route('/', methods=['GET'])
def root_func():
    # Load ip for buttons
    conf = json.load(open(os.path.join('config', 'conf.json'), 'rt'))

    return render_template('index.html', status=status_manager, connect=connect_manager, file_list=get_current_upload_list(), conf=conf)


@app.route('/rec_form/', methods=['POST'])
def rec_form():
    try:
        simple_text_log('frontend', 'Get Recommendation form view')
        simple_text_log('frontend', str(request.form))
        username = request.form['username']
        start_time = time.time()
        result = db_reader.get_recommend_with_username(username)
        simple_text_log('frontend', f'Recommendation time: {time.time()-start_time}')
        books = result['books']
        return render_template('booklist.html', books=books)
    except Exception as err:
        simple_text_log('frontend', err)
        return jsonify({'ok': False, 'info': str(err)})

@app.route('/download/<path:name>', methods=['GET'])
def download_file(name):
    if os.path.exists(os.path.join('upload', name)):
        return send_from_directory("upload", path=name, as_attachment=True)
    else:
        abort(404)

@app.route('/get_recommendation/', methods=['POST'])
def get_recommendation():
    """
        username: str
        (json)
    """
    try:
        simple_text_log('frontend', 'Get Recommendation')
        send_text = request.get_data(as_text=True)
        simple_text_log('frontend', send_text)
        send_data = json.loads(send_text)
        start_time = time.time()
        result = db_reader.get_recommend_with_username(send_data['username'])
        simple_text_log('frontend', f'Recommendation time: {time.time()-start_time}')
        return jsonify(result)
    except Exception as err:
        simple_text_log('frontend', err)
        return jsonify({'ok': False, 'info': str(err)})


@app.route('/set_db', methods=['POST'])
def set_db():
    try:
        simple_text_log('frontend', 'Set DB')
        send_text = request.get_data(as_text=True)
        simple_text_log('frontend', send_text)
        send_data = json.loads(send_text)
        db_path = 'DB/' + send_data['db_path']
        db_path = os.path.join('DB', send_data['db_path'])
        if not os.path.exists(db_path):
            raise ValueError("No such DB File!")
        db_reader.switch_to_db_file(db_path)
        return jsonify({'ok': True})
    except Exception as err:
        simple_text_log('frontend', err)
        return jsonify({'ok': False, 'info': str(err)})


@app.route('/upload_db', methods=['POST'])
def upload_db():
    try:
        simple_text_log('frontend', 'Upload DB')
        filename = request.headers.get('FileName')
        bytes_left = int(request.headers.get('content-length'))
        chunk_size = 1024 * 1024
        content = b''
        while bytes_left > 0:
            simple_text_log('frontend', "Write Chunk")
            chunk = request.stream.read(chunk_size)
            content += chunk
            bytes_left -= len(chunk)
        request.get_data()
        fdecoder = decoder.MultipartDecoder(
            content, content_type="multipart/form-data; boundary=----WebKitFromBoundaryFSDFDSJ")
        for part in fdecoder.parts:
            with open(os.path.join('DB', filename), 'wb') as f:
                f.write(part.content)

        simple_text_log('frontend', 'DB upload finish')
        return jsonify({'ok': True})
    except Exception as err:
        simple_text_log('frontend', err)
        return jsonify({'ok': False, 'info': str(err)})


@app.route('/upload_file', methods=['POST'])
def upload_file():
    file = request.files['file']

    save_path = os.path.join('upload', file.filename)
    current_chunk = int(request.form['dzchunkindex'])

    # If the file already exists it's ok if we are appending to it,
    # but not if it's new file that would overwrite the existing one
    if os.path.exists(save_path) and current_chunk == 0:
        # 400 and 500s will tell dropzone that an error occurred and show an error
        return make_response(('File already exists', 400))

    try:
        with open(save_path, 'ab') as f:
            f.seek(int(request.form['dzchunkbyteoffset']))
            f.write(file.stream.read())
    except OSError:
        return make_response(("Not sure why,"
                              " but we couldn't write the file to disk", 500))

    total_chunks = int(request.form['dztotalchunkcount'])

    if current_chunk + 1 == total_chunks:
        # This was the last chunk, the file should be complete and the size we expect
        if os.path.getsize(save_path) != int(request.form['dztotalfilesize']):
            return make_response(('Size mismatch', 500))
    return make_response(("Chunk upload successful", 200))


@app.route('/start_train', methods=['POST'])
def start_train():
    try:
        simple_text_log('frontend', 'Send start train signal to GPU Server.')

        result = connect_manager.start_training()
        if result == 0:
            simple_text_log('frontend', 'GPU Server starts successfully.')
            return jsonify({'ok': True, 'info': 'GPU Server starts successfully.'})
        elif result == 1:
            simple_text_log('frontend', 'GPU Server is still running.')
            return jsonify({'ok': False, 'info': 'GPU Server is still running.'})
        else:
            simple_text_log('frontend', 'Fail to connect to GPU Server.')
            return jsonify({'ok': False, 'info': 'Fail to connect to GPU Server.'})
    except Exception as err:
        simple_text_log('frontend', err)
        return jsonify({'ok': False, 'info': str(err)})


@app.route('/stop_train', methods=['POST'])
def stop_train():
    try:
        simple_text_log('frontend', 'Send stop train signal to GPU Server.')

        result = connect_manager.stop_training()
        if result == 0:
            simple_text_log('frontend', 'GPU Server stops successfully.')
            return jsonify({'ok': True, 'info': 'GPU Server stops successfully.'})
        else:
            simple_text_log('frontend', 'Fail to connect to GPU Server.')
            return jsonify({'ok': False, 'info': 'Fail to connect to GPU Server.'})
    except Exception as err:
        simple_text_log('frontend', err)
        return jsonify({'ok': False, 'info': str(err)})


@app.route('/upload_list', methods=['POST', 'GET'])
def upload_list():
    try:
        simple_text_log('frontend', 'Get upload file list')
        result_list = get_current_upload_list()
        return jsonify({'ok': True, 'files': result_list})
    except Exception as err:
        simple_text_log('frontend', err)
        return jsonify({'ok': False, 'info': str(err)})


@app.route('/delete_file_view', methods=['POST'])
def delete_file_view():
    try:
        simple_text_log('frontend', 'Delete file')
        filename = os.path.join('upload', request.form['delete'])
        if not os.path.exists(filename):
            raise ValueError(f"File {filename} not exists!")
        os.remove(filename)
        return jsonify({'ok': True})
    except Exception as err:
        simple_text_log('frontend', err)
        return jsonify({'ok': False, 'info': str(err)})


@app.route('/delete_file', methods=['POST'])
def delete_file():
    """
        filename: str
        (json)
    """
    try:
        simple_text_log('frontend', 'Delete file')
        send_text = request.get_data(as_text=True)
        simple_text_log('frontend', send_text)
        send_data = json.loads(send_text)
        if 'filename' not in send_data:
            raise ValueError(f"Param filename not in POST json data")

        filename = os.path.join('upload', send_data['filename'])

        if not os.path.exists(filename):
            raise ValueError(f"File {filename} not exists!")
        os.remove(filename)
        return jsonify({'ok': True})
    except Exception as err:
        simple_text_log('frontend', err)
        return jsonify({'ok': False, 'info': str(err)})


if __name__ == '__main__':
    app.run(host='0.0.0.0', port='12679', debug=True)
